package com.ly.blog_common.security;


import com.alibaba.fastjson.JSON;
import com.ly.blog_common.constant.Constant;
import com.ly.blog_common.entity.SysUserEntity;
import com.ly.blog_common.entity.SysUserTokenEntity;
import com.ly.blog_common.utils.RequestUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * Security相关操作
 * @author Louis
 * @date Jan 14, 2019
 */
@Slf4j
public class SecurityUtils {


    /**
     * 检测token值有效性 和 维护token时间 - 全局赋值当前登录状态
     *  @param request
     * @param redisTemplate
     */
    public static void checkAndUpdateToken(HttpServletRequest request, RedisTemplate redisTemplate) {

        Authentication authentication = null;
        String token = RequestUtils.getToken(request);
        if (token != null) {
            try {
                SysUserTokenEntity sysUserTokenEntity =
                        (SysUserTokenEntity) redisTemplate.opsForValue().get(token);
                if (sysUserTokenEntity != null) {
                    authentication = getAndUpdateAuthenticationFromToken(
                            sysUserTokenEntity,redisTemplate
                    );
                }
            }catch (Exception e){
                log.error("【redis获取token】token存在，但解析过程异常，异常信息：{}",e.getMessage());
            }
        }
        // 设置登录认证信息到上下文
        if(authentication instanceof  JwtAuthenticationToken){
            JwtAuthenticationToken jwtAuthenticationToken = (JwtAuthenticationToken) authentication;
            jwtAuthenticationToken.setToken(token);
        }
        setAuthentication(authentication);
    }

    /**
     * 检测当前登录情况
     * token为空，令牌null
     * token不为空
     * 检测当前登录状态令牌
     * 无：token反推
     * 有：检测有效性
     * @param sysUserTokenEntity
     * @param redisTemplate
     * @return 最后返回令牌对象
     */

    private static Authentication getAndUpdateAuthenticationFromToken(SysUserTokenEntity sysUserTokenEntity, RedisTemplate redisTemplate) {
        Authentication authentication = null;

        //更新token
        String token = sysUserTokenEntity.getToken();

        //会出现不是当前对象的问题
        JwtUserDetails jwtUserDetails = null;
        try {
            jwtUserDetails = JSON.parseObject(sysUserTokenEntity.getUserDetail(), JwtUserDetails.class);
        } catch (Exception e) {
            log.info("【token解析】解析JwtUserDetails异常，{}", e.getMessage());
        }
        // 请求令牌判定 空通过token解析，非空判定两个用户是否一致
        //这里存在一个问题，因为存在两个服务，token和当前上下文鉴权对象对应不一致，单服务不存在这个问题
        if ((authentication = getAuthentication()) != null) {

            //校验两个对象的值 是否为同一个用户
            JwtUserDetails jwtUserDetails1 = parseJwtUserDetails(authentication.getPrincipal());


            if (jwtUserDetails1 == null || !jwtUserDetails.getUser().getUserId().equals(jwtUserDetails1.getUser().getUserId()) ) {
                log.info("【token解析】 - 用户和已登录不一致....");
                return null;
            }

            //检验当前上下文和当前token是否对应 - 不对应当做没有上下文处理
            if(authentication instanceof  JwtAuthenticationToken ){
                JwtAuthenticationToken jwtAuthenticationToken = (JwtAuthenticationToken) authentication;
                if(!token.equals(jwtAuthenticationToken.getToken())){
                    authentication = null;
                }
            }
        }

        // 这一步需要上一步判定authentication 空性，存在authentication有，但不是上下文，需要重置
        if(authentication == null) {
            if (jwtUserDetails == null) {
                log.info("【token解析】 - token解析认证对象失败....");
                return null;
            }
            if (System.currentTimeMillis() > sysUserTokenEntity.getExpireTime().getTime()) {
                log.info("【token解析】 - token已过期....");
                return null;
            }
            authentication = new JwtAuthenticationToken(jwtUserDetails, null, jwtUserDetails.getAuthorities());
        }

        //重置token时长
        Date expireTime = new Date(System.currentTimeMillis() + Constant.EXPIRE * 1000);
        sysUserTokenEntity.setUpdateTime(new Date());
        sysUserTokenEntity.setExpireTime(expireTime);

        //redis 更新token
        redisTemplate.opsForValue().set(String.valueOf(token), sysUserTokenEntity, Constant.EXPIRE, TimeUnit.SECONDS);

        return authentication;
    }

    //默认 principal 字符串
    private static JwtUserDetails parseJwtUserDetails(Object principal) {
        if(principal instanceof JwtUserDetails){
            return (JwtUserDetails) principal;
        }
        return null;
    }

    public static Authentication getAuthentication() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        return authentication;
    }

    public static void setAuthentication(Authentication authentication) {
        SecurityContextHolder.getContext().setAuthentication(authentication);
    }

    /**
     * 获取用户权限集合
     * @return
     */
    public static Set<String> getPermissions() {
        Authentication authentication = getAuthentication();
        if (authentication == null){
            return null;
        }
        return authentication.getAuthorities().stream().map(grantedAuthority -> ((GrantedAuthority) grantedAuthority).getAuthority())
                .collect(Collectors.toSet());
    }

    //获取当前登录用户id
    public static String getUserId() {
        Authentication authentication = getAuthentication();
        if (authentication == null){
            return null;
        }
        JwtUserDetails jwtUserDetails1 = parseJwtUserDetails(authentication.getPrincipal());
        if (jwtUserDetails1 == null){
            return null;
        }
        return String.valueOf(jwtUserDetails1.getUserId());
    }

    //获取当前登录用户id
    public static String getUsername() {
        Authentication authentication = getAuthentication();
        if (authentication == null){
            return null;
        }
        JwtUserDetails jwtUserDetails1 = parseJwtUserDetails(authentication.getPrincipal());
        if (jwtUserDetails1 == null){
            return null;
        }
        return String.valueOf(jwtUserDetails1.getUsername());
    }

    //获取当前登录用户对象 - 登录时存储
    public static SysUserEntity getUser() {
        Authentication authentication = getAuthentication();
        if (authentication == null){
            return null;
        }
        JwtUserDetails jwtUserDetails1 = parseJwtUserDetails(authentication.getPrincipal());
        if (jwtUserDetails1 == null){
            return null;
        }
        return jwtUserDetails1.getUser();
    }

}
